//
//  WDAlertTipsController.swift
//  Pods-Example
//
//  Created by gupengling on 2021/5/12.
//

import Foundation
import SnapKit

@objcMembers
public class WDAlertConfig: NSObject {
    public var cornerRadius: CGFloat = 10.0
    public var alertMaxHeight: CGFloat = 300.0
    public var alertBackgroundColor: UIColor = .white
    public var overlayViewBackgroundColor: UIColor = UIColor(red:0, green:0, blue:0, alpha:0.5)
}

//@objcMembers
public class WDAlertButtonStyle: NSObject {
    public var font = UIFont.systemFont(ofSize: 18)
    public var titleColor = UIColor.black
    public var buttonBgColor = UIColor.white
    public var buttonHighlightedBgColor = UIColor.white.withAlphaComponent(0.5)

    public var style: WDAlertTipsActionStyle = .default {
        didSet {
            if style == .cancel {
                self.font = UIFont.boldSystemFont(ofSize: 20)
                self.titleColor = UIColor.white
                self.buttonBgColor = UIColor.red
                self.buttonHighlightedBgColor = UIColor.red.withAlphaComponent(0.5)
            } else if style == .destructive {
                self.font = UIFont.systemFont(ofSize: 20)
                self.titleColor = UIColor.white
                self.buttonBgColor = UIColor.darkGray
                self.buttonHighlightedBgColor = UIColor.darkGray.withAlphaComponent(0.5)
            }
        }
    }

}

@objc public enum WDAlertTipsActionStyle : Int {
    case `default`
    case cancel
    case destructive
}

@objc(WDAlertTipsAction)
open class WDAlertTipsAction: NSObject, NSCopying {
    @objc fileprivate(set) var handler: ((WDAlertTipsAction?) -> Void)?
    @objc open fileprivate(set) var title: String = "Default"
    @objc open fileprivate(set) var style: WDAlertTipsActionStyle
    @objc open var enabled: Bool
//    {
//        didSet {
//            if (oldValue != enabled) {
//            }
//        }
//    }
    @objc public required init(title: String,
                         style: WDAlertTipsActionStyle,
                         handler: ((WDAlertTipsAction?) -> Void)?) {
        self.title = title
        self.style = style
        self.handler = handler
        self.enabled = true
    }

    public func copy(with zone: NSZone? = nil) -> Any {
        let copy = type(of: self).init(title: title, style: style, handler: handler)
        copy.enabled = self.enabled
        return copy
    }
}

@objc public enum WDAlertTipsControllerStyle : Int {
    case actionSheet
    case alert
}

@objc(WDAlertTipsController)
open class WDAlertTipsController: UIViewController {
    open fileprivate(set) var alertTitle: String?
    open fileprivate(set) var alertOtherTitle: String?
    @objc open var config = WDAlertConfig()
    @objc open var tapClose = true

    private var _headerContentView: UIView?
    @objc public var headerContentView: (() -> UIView?)?

    private var _bodyContentView: UIView?
    @objc public var bodyContentView: (() -> UIView?)?

    private var _footerContentView: UIView?
    @objc public var footerContentView: (() -> UIView?)?

    @objc open var alertHeaderView = UIView()

    @objc open var alertView: UIView = {
        let view = UIView()
        view.backgroundColor = UIColor(red:239/255, green:240/255, blue:242/255, alpha:1.0)
        return view
    }()

    @objc open var alertFooterView = UIView()
    @objc open var buttonsView = UIView()

    open fileprivate(set) var preferredStyle: WDAlertTipsControllerStyle?

    fileprivate var containerView = UIView()
    fileprivate lazy var overlayView: UIView = {
        let view = UIView()
        view.backgroundColor = UIColor(red:0, green:0, blue:0, alpha:0.5)
        return view
    }()

    fileprivate var alertTitleLabel: UILabel = {
        let label = UILabel()
        label.font = .boldSystemFont(ofSize: 20)
        label.textColor = .black
        label.textAlignment = .center
        return label
    }()

    fileprivate var alertOtherTitleLabel: UILabel = {
        let label = UILabel()
        label.font = .systemFont(ofSize: 16)
        label.textColor = .lightGray
        label.textAlignment = .center
        return label
    }()

    fileprivate var alertTitleView: UIView = {
        let view = UIView()
        view.backgroundColor = .clear
        return view
    }()

    open fileprivate(set) var actions: [AnyObject] = []
    open var buttons = [UIButton]()

    fileprivate var areaScrollView = UIScrollView()

    fileprivate var containerViewBottomSpaceConstraint: NSLayoutConstraint?
//    fileprivate var alertViewHeightConstraint: NSLayoutConstraint?
    open var alertViewWidth: CGFloat = 300.0
    fileprivate var alertViewPadding: CGFloat = 5.0
    fileprivate var alertTitlePadding: CGFloat = 20.0
    fileprivate let actionSheetBounceHeight: CGFloat = 25.0

    fileprivate var layoutFlg = false
    fileprivate var cancelButtonTag = 0

    @objc public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName:nibNameOrNil, bundle:nibBundleOrNil)
    }

    deinit {
        print("WDAlertTipsController deinit")
    }

    @objc public convenience init(alertTitle: String? = nil, alertOtherTitle: String? = nil, preferredStyle: WDAlertTipsControllerStyle) {
        self.init(nibName: nil, bundle: nil)
        self.alertTitle = alertTitle
        self.alertOtherTitle = alertOtherTitle
        self.preferredStyle = preferredStyle

        self.providesPresentationContextTransitionStyle = true
        self.definesPresentationContext = true
        self.modalPresentationStyle = .custom
        self.transitioningDelegate = self
    }

    public required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    open override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        if (tapClose) {
            let tapGesture = UITapGestureRecognizer(target: self, action: #selector(WDAlertTipsController.handleContainerViewTapGesture(_:)))
            containerView.addGestureRecognizer(tapGesture)
        }
    }
    
    open override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        if let bodyView = _bodyContentView {
            bodyView.layoutIfNeeded()
//            print("viewDidLayoutSubviews \(bodyView.frame)")
            let x : CGFloat = isAlert() ? 0.8 : 1.0
            let max = config.alertMaxHeight * x
            let bodyHeight = bodyView.frame.size.height < max ? bodyView.frame.size.height : max
            areaScrollView.snp.updateConstraints { make in
                make.height.equalTo(bodyHeight)
            }
        }
    }

    open override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        layoutView(presentingViewController)
    }

    open func layoutView(_ presenting: UIViewController?) {
        if (layoutFlg) { return }
        layoutFlg = true
        let screenSize = presenting != nil ? presenting!.view.bounds.size : UIScreen.main.bounds.size

        overlayView.backgroundColor = config.overlayViewBackgroundColor
        alertView.backgroundColor = config.alertBackgroundColor
        alertView.layer.cornerRadius = config.cornerRadius
        alertView.layer.masksToBounds = true
        view.frame.size = screenSize
        view.addSubview(overlayView)
        view.addSubview(containerView)
        containerView.addSubview(alertView)

        let hasTitle: Bool = alertTitle != nil && alertTitle != ""
        let hasOtherTitle: Bool = alertOtherTitle != nil && alertOtherTitle != ""
        if hasTitle || hasOtherTitle {
            alertView.addSubview(alertTitleView)
            alertTitleView.snp.makeConstraints { make in
                make.left.right.top.equalTo(alertView)
            }
            if hasTitle {
                alertTitleLabel.text = alertTitle
                alertTitleView.addSubview(alertTitleLabel)
                alertTitleLabel.snp.makeConstraints { make in
                    make.left.right.equalTo(alertTitleView)
                    make.top.equalTo(alertTitleView.snp.top).offset(alertTitlePadding)
                    if !hasOtherTitle {
                        make.bottom.equalTo(alertTitleView.snp.bottom).offset(-alertTitlePadding)
                    }
                }
            }
            if hasOtherTitle {
                alertOtherTitleLabel.text = alertOtherTitle
                alertTitleView.addSubview(alertOtherTitleLabel)
                alertOtherTitleLabel.snp.makeConstraints { make in
                    make.left.right.equalTo(alertTitleView)
                    if !hasTitle {
                        make.top.equalTo(alertTitleView.snp.top).offset(alertTitlePadding)
                    } else {
                        make.top.equalTo(alertTitleLabel.snp.bottom).offset(alertViewPadding)
                    }
                    make.bottom.equalTo(alertTitleView.snp.bottom).offset(-alertTitlePadding)
                }
            }
        }

        alertView.addSubview(alertHeaderView)
        alertView.addSubview(areaScrollView)
        alertView.addSubview(alertFooterView)
        alertView.addSubview(buttonsView)

        if _headerContentView == nil {
            _headerContentView = headerContentView?()
            if let headerView = _headerContentView {
                alertHeaderView.addSubview(headerView)
                headerView.snp.makeConstraints { make in
                    make.edges.equalToSuperview()
                }
            }
        }

        alertHeaderView.snp.makeConstraints { make in
            make.left.right.equalTo(alertView)
            if hasTitle || hasOtherTitle {
                make.top.equalTo(alertTitleView.snp.bottom)
            } else {
                make.top.equalTo(alertView)
            }
            if _headerContentView == nil {
                make.height.equalTo(0)
            }
        }

        if _bodyContentView == nil {
            _bodyContentView = bodyContentView?()
            if let headerView = _bodyContentView {
                areaScrollView.addSubview(headerView)
                headerView.snp.makeConstraints { make in
                    make.left.right.equalTo(alertView)
                    make.top.bottom.equalTo(areaScrollView)
//                    make.edges.equalToSuperview()
                }
            }
        }

        areaScrollView.snp.makeConstraints { make in
            make.left.right.equalTo(alertView)
            make.top.equalTo(alertHeaderView.snp.bottom)
//            make.bottom.equalTo(alertFooterView.snp_top)
//            make.height.greaterThanOrEqualTo(300)
            make.height.equalTo(0)
        }

        if _footerContentView == nil {
            _footerContentView = footerContentView?()
            if let footerView = _footerContentView {
                alertFooterView.addSubview(footerView)
                footerView.snp.makeConstraints { make in
                    make.edges.equalToSuperview()
                }
            }
        }

        alertFooterView.snp.makeConstraints { make in
            make.left.right.equalTo(alertView)
            make.top.equalTo(areaScrollView.snp.bottom)
            if _footerContentView == nil {
                make.height.equalTo(0)
            }
        }

        let bottonBounce = isAlert() ? 0 : -alertViewPadding
        buttonsView.snp.makeConstraints { make in
            make.left.right.equalTo(alertView)
            make.top.equalTo(alertFooterView.snp.bottom)
//            make.bottom.equalTo(alertView.snp_bottom)
            if !isAlert(), #available(iOS 11.0, *) {
                make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom).offset(bottonBounce)
            } else {
                make.bottom.equalTo(alertView).offset(bottonBounce)
            }

            if buttons.isEmpty {
                make.height.equalTo(0)
            }
        }

        overlayView.translatesAutoresizingMaskIntoConstraints = false
        containerView.translatesAutoresizingMaskIntoConstraints = false
        alertView.translatesAutoresizingMaskIntoConstraints = false
        alertHeaderView.translatesAutoresizingMaskIntoConstraints = false
        areaScrollView.translatesAutoresizingMaskIntoConstraints = false
        alertFooterView.translatesAutoresizingMaskIntoConstraints = false
        buttonsView.translatesAutoresizingMaskIntoConstraints = false

        let overlayViewTopSpaceConstraint = NSLayoutConstraint(item: overlayView, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1.0, constant: 0.0)
        let overlayViewRightSpaceConstraint = NSLayoutConstraint(item: overlayView, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1.0, constant: 0.0)
        let overlayViewLeftSpaceConstraint = NSLayoutConstraint(item: overlayView, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1.0, constant: 0.0)
        let overlayViewBottomSpaceConstraint = NSLayoutConstraint(item: overlayView, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: 0.0)
        let containerViewTopSpaceConstraint = NSLayoutConstraint(item: containerView, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1.0, constant: 0.0)
        let containerViewRightSpaceConstraint = NSLayoutConstraint(item: containerView, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1.0, constant: 0.0)
        let containerViewLeftSpaceConstraint = NSLayoutConstraint(item: containerView, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1.0, constant: 0.0)
        containerViewBottomSpaceConstraint = NSLayoutConstraint(item: containerView, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: 0.0)
        view.addConstraints([overlayViewTopSpaceConstraint, overlayViewRightSpaceConstraint, overlayViewLeftSpaceConstraint, overlayViewBottomSpaceConstraint, containerViewTopSpaceConstraint, containerViewRightSpaceConstraint, containerViewLeftSpaceConstraint, containerViewBottomSpaceConstraint!])

        if isAlert() {
            let alertViewCenterXConstraint = NSLayoutConstraint(item: alertView, attribute: .centerX, relatedBy: .equal, toItem: containerView, attribute: .centerX, multiplier: 1.0, constant: 0.0)
            yConstraints = NSLayoutConstraint(item: alertView, attribute: .centerY, relatedBy: .equal, toItem: containerView, attribute: .centerY, multiplier: 1.0, constant: 0.0)
            containerView.addConstraints([alertViewCenterXConstraint, yConstraints!])

            let alertViewWidthConstraint = NSLayoutConstraint(item: alertView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1.0, constant: alertViewWidth)
            alertView.addConstraints([alertViewWidthConstraint])
//            alertViewHeightConstraint = NSLayoutConstraint(item: alertView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1.0, constant: 1000.0)
//            alertView.addConstraints([alertViewWidthConstraint, alertViewHeightConstraint!])
        } else {
            let alertViewCenterXConstraint = NSLayoutConstraint(item: alertView, attribute: .centerX, relatedBy: .equal, toItem: containerView, attribute: .centerX, multiplier: 1.0, constant: 0.0)
            let alertViewBottomSpaceConstraint = NSLayoutConstraint(item: alertView, attribute: .bottom, relatedBy: .equal, toItem: containerView, attribute: .bottom, multiplier: 1.0, constant: actionSheetBounceHeight)
            let alertViewWidthConstraint = NSLayoutConstraint(item: alertView, attribute: .width, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: 1.0, constant: 0.0)
            containerView.addConstraints([alertViewCenterXConstraint, alertViewBottomSpaceConstraint, alertViewWidthConstraint])

//            alertViewHeightConstraint = NSLayoutConstraint(item: alertView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1.0, constant: 1000.0)
//            alertView.addConstraint(alertViewHeightConstraint!)
        }

        for (index, button) in buttons.enumerated() {
            if let action = actions[safe: button.tag - 1] as? WDAlertTipsAction {
                let style = WDAlertButtonStyle()
                style.style = action.style
                button.titleLabel?.font = style.font
                button.setTitleColor(style.titleColor, for: .normal)
                button.setBackgroundImage(UIImage.createImageFromUIColor(style.buttonBgColor), for: .normal)
                button.setBackgroundImage(UIImage.createImageFromUIColor(style.buttonHighlightedBgColor), for: .highlighted)
                button.setBackgroundImage(UIImage.createImageFromUIColor(style.buttonHighlightedBgColor), for: .selected)
                button.snp.makeConstraints { make in
                    make.left.equalTo(buttonsView.snp.left).offset(alertViewPadding)
                    make.right.equalTo(buttonsView.snp.right).offset(-alertViewPadding)
                    make.top.equalTo(alertFooterView.snp.bottom).offset(CGFloat(50 * index) + alertViewPadding * CGFloat(index + 1))
                    make.height.equalTo(50)
                    if index + 1 == buttons.count {
                        make.bottom.equalTo(buttonsView.snp.bottom).offset(-alertViewPadding)
                    }
                }
            }
        }

//        reloadAlertViewHeight()
//        alertView.frame.size = CGSize(width: alertViewWidth, height: alertViewHeightConstraint?.constant ?? 150)
    }

    private var yConstraints: NSLayoutConstraint?
    //MARK: open
    @objc open func updateYConstant(constant: CGFloat) {
        if isAlert() {
            yConstraints?.constant = constant
            containerView.updateConstraintsIfNeeded()
        }
    }

    @objc open func addAction(_ action: WDAlertTipsAction) {
        if (action.style == .cancel) {
            if let actions = actions as? [WDAlertTipsAction],
               let _ = actions.firstIndex(where: { action in
                action.style == .cancel
               }) {
                let error: NSError? = nil
                NSException.raise(NSExceptionName(rawValue: "NSInternalInconsistencyException"), format:"WDAlertTipsController can only have one action with a style of WDAlertTipsActionStyleCancel", arguments:getVaList([error ?? "nil"]))
                return

            }
        }
        actions.append(action)

        let button = UIButton()
        button.layer.masksToBounds = true
        button.setTitle(action.title, for: .normal)
        button.isEnabled = action.enabled
        button.layer.cornerRadius = 8
        button.addTarget(self, action: #selector(WDAlertTipsController.buttonTapped(_:)), for: .touchUpInside)
        button.tag = buttons.count + 1
        buttons.append(button)
        buttonsView.addSubview(button)
    }

    @objc func handleContainerViewTapGesture(_ sender: AnyObject) {
        close(actions[safe: (cancelButtonTag - 1)] as? WDAlertTipsAction)
    }

    @objc func buttonTapped(_ sender: UIButton) {
        sender.isSelected = true
        close(actions[safe: (sender.tag - 1)] as? WDAlertTipsAction)
    }

    @objc open func close(_ action: WDAlertTipsAction? = nil) {
        if let action = action,
           let handler = action.handler {
            handler(action)
        }
        dismiss(animated: true, completion: nil)
    }
    
    @objc open func isAlert() -> Bool {
        return preferredStyle == .alert
    }

    open override var shouldAutorotate: Bool {
        return true
    }

    open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return self.presentingViewController?.supportedInterfaceOrientations ?? .portrait
    }

    open override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
        return self.presentingViewController?.preferredInterfaceOrientationForPresentation ?? .portrait
    }
}

// MARK: UIViewControllerTransitioningDelegate
extension WDAlertTipsController: UIViewControllerTransitioningDelegate {
    open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        layoutView(presenting)
        return WDAlertTipsAnimation(isPresenting: true)
    }

    open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return WDAlertTipsAnimation(isPresenting: false)
    }
}

// MARK: 动画
@objc(WDAlertTipsAnimation)
open class WDAlertTipsAnimation : NSObject, UIViewControllerAnimatedTransitioning {
    @objc let isPresenting: Bool
    @objc public init(isPresenting: Bool) {
        self.isPresenting = isPresenting
    }

    open func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        if (isPresenting) {
            return 0.45
        } else {
            return 0.25
        }
    }

    open func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        if (isPresenting) {
            presentAnimateTransition(transitionContext)
        } else {
            dismissAnimateTransition(transitionContext)
        }
    }

    private func presentAnimateTransition(_ transitionContext: UIViewControllerContextTransitioning) {
        guard let alertController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) as? WDAlertTipsController  else {
            return
        }
        let containerView = transitionContext.containerView
        alertController.alertView.layoutIfNeeded()
        alertController.overlayView.alpha = 0.0
        if (alertController.isAlert()) {
            alertController.alertView.alpha = 0.0
            alertController.alertView.center = alertController.view.center
            alertController.alertView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
        } else {
//            print("presentAnimateTransition \(alertController.alertView.frame.height), \(String(describing: alertController.alertView.constraints.first?.constant))")
            alertController.alertView.transform = CGAffineTransform(translationX: 0, y: alertController.alertView.frame.height)
        }
        containerView.addSubview(alertController.view)

        UIView.animate(withDuration: 0.25,
                       animations: {
                        alertController.overlayView.alpha = 1.0
                        if (alertController.isAlert()) {
                            alertController.alertView.alpha = 1.0
                            alertController.alertView.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
                        } else {
                            let bounce = alertController.alertView.frame.height / 480 * 10.0 + 10.0
//                            print("presentAnimateTransition \(bounce)")
                            alertController.alertView.transform = CGAffineTransform(translationX: 0, y: -bounce)
                        }
                       }, completion: { finished in
                        UIView.animate(withDuration: 0.2,
                                       animations: {
                                        alertController.alertView.transform = CGAffineTransform.identity
                                       },
                                       completion: { finished in
                                        if (finished) {
                                            transitionContext.completeTransition(true)
                                        }
                                       })
                       })
    }

    private func dismissAnimateTransition(_ transitionContext: UIViewControllerContextTransitioning) {
        guard let alertController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) as? WDAlertTipsController  else {
            return
        }
        UIView.animate(withDuration: transitionDuration(using: transitionContext),
                       animations: {
                        alertController.overlayView.alpha = 0.0
                        if (alertController.isAlert()) {
                            alertController.alertView.alpha = 0.0
                            alertController.alertView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
                        } else {
                            alertController.containerView.transform = CGAffineTransform(translationX: 0, y: alertController.alertView.frame.height)
                        }
                       }, completion: { finished in
                        transitionContext.completeTransition(true)
                       })
    }
}

//public extension Array {
//    subscript(safe index: Int) -> Element? {
//        return indices ~= index ? self[index] : .none
//    }
//}
public extension Collection {
    subscript(safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

public extension UIImage {
    class func createImageFromUIColor(_ color: UIColor) -> UIImage {
        let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
        UIGraphicsBeginImageContext(rect.size)
        let contextRef: CGContext = UIGraphicsGetCurrentContext()!
        contextRef.setFillColor(color.cgColor)
        contextRef.fill(rect)
        let img: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return img
    }
}
